﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EuroManager.Domain.Simulator.Engine;
using EuroManager.Domain.Simulator.Helpers;
using NUnit.Framework;

namespace EuroManager.Domain.Simulator.Tests.Engine
{
    [TestFixture]
    public class Vector2DTests
    {
        [Test]
        public void ShouldBeZeroWhenBothCoordinatesAreZero()
        {
            Vector2D vector = new Vector2D(0, 0);

            Assert.IsTrue(vector.IsZero);
        }

        [Test]
        public void ShouldNotBeZeroWhenOneCoordinateIsNotZero()
        {
            Vector2D vector = new Vector2D(0, -0.0001f);

            Assert.IsFalse(vector.IsZero);
        }

        [Test]
        public void ShouldExposeLength()
        {
            Vector2D vector = new Vector2D(3, 4);

            Assert.AreEqual(5, vector.Length, Maths.Epsilon);
        }

        [Test]
        public void ShouldBeNormalizedWhenLengthEqualsOne()
        {
            Vector2D vector = new Vector2D(3.0000001f, 4) / 5;

            Assert.IsTrue(vector.IsNormalized);
        }

        [Test]
        public void ShouldNotBeNormalizedWhenLengthNotEqualsOne()
        {
            Vector2D vector = new Vector2D(3.001f, 4) / 5;

            Assert.IsFalse(vector.IsNormalized);
        }

        [Test]
        public void ShouldCalculateDotProduct()
        {
            Vector2D v1 = new Vector2D(1, 2);
            Vector2D v2 = new Vector2D(-2, 3);

            Assert.AreEqual(4, Vector2D.Dot(v1, v2), Maths.Epsilon);
        }

        [Test]
        public void ShouldCalculateCrossProduct()
        {
            Vector2D v1 = new Vector2D(1, 2);
            Vector2D v2 = new Vector2D(-2, 3);

            Assert.AreEqual(7, Vector2D.Cross(v1, v2), Maths.Epsilon);
        }

        [Test]
        public void ShouldEqualForEqualCoordinates()
        {
            Vector2D v1 = new Vector2D(1, -2);
            Vector2D v2 = new Vector2D(1, -2);

            Assert.IsTrue(v1 == v2);
            Assert.IsFalse(v1 != v2);
        }

        [Test]
        public void ShouldNotEqualForNotEqualCoordinates()
        {
            Vector2D v1 = new Vector2D(1, -2);
            Vector2D v2 = new Vector2D(1.00001f, -2);

            Assert.IsFalse(v1 == v2);
            Assert.IsTrue(v1 != v2);
        }

        [Test]
        public void ShouldAddVectors()
        {
            Vector2D v1 = new Vector2D(1, 2);
            Vector2D v2 = new Vector2D(-2, 3);

            Assert.AreEqual(new Vector2D(-1, 5), v1 + v2);
        }

        [Test]
        public void ShouldSubtractVectors()
        {
            Vector2D v1 = new Vector2D(1, 2);
            Vector2D v2 = new Vector2D(-2, 3);

            Assert.AreEqual(new Vector2D(3, -1), v1 - v2);
        }

        [Test]
        public void ShouldNegate()
        {
            Vector2D v = new Vector2D(-1, 2);

            Assert.AreEqual(new Vector2D(1, -2), -v);
        }

        [Test]
        public void ShouldMultiply()
        {
            Vector2D v = new Vector2D(-1, 2);

            Assert.AreEqual(new Vector2D(3, -6), -3 * v);
        }

        [Test]
        public void ShouldDivide()
        {
            Vector2D v = new Vector2D(-1, 2);

            Assert.AreEqual(new Vector2D(-0.25f, 0.5f), v / 4);
        }

        [Test]
        public void ShouldEqual()
        {
            Vector2D v1 = new Vector2D(1, -2);
            Vector2D v2 = new Vector2D(1, -2);

            Assert.IsTrue(v1.Equals(v2));
        }

        [Test]
        public void ShouldNotEqualWithNull()
        {
            Vector2D v = new Vector2D(-2, 3);

            Assert.IsFalse(v.Equals(null));
        }

        [Test]
        public void ShouldNotEqualWithDifferentType()
        {
            Vector2D v = new Vector2D(-2, 3);

            Assert.IsFalse(v.Equals(new object()));
        }

        [Test]
        public void ShouldEqualWithDelta()
        {
            Vector2D v1 = new Vector2D(1, -2);
            Vector2D v2 = new Vector2D(0.98f, -2.03f);

            Assert.IsTrue(Vector2D.Equals(v1, v2, 0.05f));
        }

        [Test]
        public void ShouldBeParallel()
        {
            Vector2D v1 = new Vector2D(-3, 1);
            Vector2D v2 = new Vector2D(7.5f, -2.5f);

            Assert.IsTrue(v1.IsParallel(v2));
        }

        [Test]
        public void ShouldNotBeParallel()
        {
            Vector2D v1 = new Vector2D(-3, 1);
            Vector2D v2 = new Vector2D(-7.51f, 2.5f);

            Assert.IsFalse(v1.IsParallel(v2));
        }

        [Test]
        public void ShouldBeParallelWithSense()
        {
            Vector2D v1 = new Vector2D(3, -1);
            Vector2D v2 = new Vector2D(7.5f, -2.5f);

            Assert.IsTrue(v1.IsParallelWithSense(v2));
        }

        [Test]
        public void ShouldNotBeParallelWithSense()
        {
            Vector2D v1 = new Vector2D(3, -1.001f);
            Vector2D v2 = new Vector2D(7.5f, -2.5f);

            Assert.IsFalse(v1.IsParallelWithSense(v2));
        }

        [Test]
        public void ShouldNotBeParallelWithSenseWhenSenseIsReversed()
        {
            Vector2D v1 = new Vector2D(-3, 1);
            Vector2D v2 = new Vector2D(7.5f, -2.5f);

            Assert.IsFalse(v1.IsParallelWithSense(v2));
        }

        [Test]
        public void ShouldBePerpendicular()
        {
            Vector2D v1 = new Vector2D(-3, 1);
            Vector2D v2 = new Vector2D(2.5f, 7.5f);

            Assert.IsTrue(v1.IsPerpendicular(v2));
        }

        [Test]
        public void ShouldNotBePerpendicular()
        {
            Vector2D v1 = new Vector2D(-3, 1.1f);
            Vector2D v2 = new Vector2D(2.5f, 7.5f);

            Assert.IsFalse(v1.IsPerpendicular(v2));
        }

        [Test]
        public void ShouldNormalizeZeroVector()
        {
            Vector2D result = Vector2D.Zero.Normalized();

            Assert.IsTrue(result.IsZero);
        }

        [Test]
        public void ShouldHaveLengthOfOneWhenNormalized()
        {
            Vector2D v = new Vector2D(3, -5);

            Assert.AreEqual(1, v.Normalized().Length, Maths.Epsilon);
        }

        [Test]
        public void ShouldNormalize()
        {
            Vector2D v = new Vector2D(1, -1);
            Vector2D expected = new Vector2D(1 / Maths.Sqrt(2), -1 / Maths.Sqrt(2));

            Assert.IsTrue(Vector2D.Equals(expected, v.Normalized(), Maths.Epsilon));
        }

        [Test]
        public void ShouldTruncate()
        {
            Vector2D v = new Vector2D(6, -8);
            Vector2D expected = new Vector2D(3, -4);

            Assert.IsTrue(Vector2D.Equals(expected, v.Truncated(5), Maths.Epsilon));
        }

        [Test]
        public void ShouldNotTruncateIfLengthIsCorrect()
        {
            Vector2D v = new Vector2D(6, -8);

            Assert.AreEqual(v, v.Truncated(10));
        }

        [Test]
        public void ShouldCreatePerpendicularVector()
        {
            Vector2D v = new Vector2D(3, -2);

            Assert.AreEqual(new Vector2D(2, 3), v.Perpendicular());
        }

        [Test]
        public void ShouldRotate()
        {
            Vector2D v = new Vector2D(2.5f, -1.9f);
            Vector2D rotated = v.Rotated(Maths.Pi / 2);

            Assert.IsTrue(Vector2D.Equals(v.Perpendicular(), rotated, Maths.Epsilon));
        }

        [Test]
        public void ShouldRotateZeroVector()
        {
            Assert.AreEqual(Vector2D.Zero, Vector2D.Zero.Rotated(Maths.Pi / 2));
        }
    }
}
